﻿using Digitalmes.Application.Mes;
using Digitalmes.Domain;
using Digitalmes.Domain.Enums;
using Digitalmes.Domain.Mes;
using Digitalmes.Scada.Models;
using Digitalmes.Sugar;

namespace Digitalmes.Scada.Services.Impl;

/// <summary>
/// 扫关键物料服务
/// </summary>
internal sealed class ScanBarcodeService : IScanBarcodeService, ITransientDependency
{
    private readonly SugarRepository<ProdTransSn> _tranSnRepo;
    private readonly SugarRepository<ProdTransSnRecord> _tranSnRecordRepo;
    private readonly SugarRepository<MdItem> _itemRepo;
    private readonly SugarRepository<ProdMaterialConsume> _materialConsumeRepo;
    private readonly ProcBomCheckService _procBomCheckService;

    private readonly ProcessQueryService _processQuery;

    public ScanBarcodeService(SugarRepository<ProdTransSn> tranSnRepo,
        SugarRepository<ProdTransSnRecord> tranSnRecordRepo,
        SugarRepository<MdItem> itemRepo,
        SugarRepository<ProdMaterialConsume> materialConsumeRepo,
        ProcBomCheckService procBomCheckService,
        ProcessQueryService processQuery)
    {
        _tranSnRepo = tranSnRepo;
        _tranSnRecordRepo = tranSnRecordRepo;
        _itemRepo = itemRepo;
        _materialConsumeRepo = materialConsumeRepo;
        _procBomCheckService = procBomCheckService;
        _processQuery = processQuery;
    }

    public async Task<HandleResult> ScanAsync(ScanInput input)
    {
        // 通过 SN 找到当前所处工位的产品，校验产品设置的规则集合，满足任一一个即可。
        // 找到当前工位最近一条处于进站状态的数据。
        var transSn = await _tranSnRepo.GetFirstAsync(s => s.CurLineCode == input.LineCode
                 && s.CurOpCode == input.OpCode && s.Stage == SnStageEnum.In, m => m.InboundTime, Common.Enums.OrderEnum.Desc);
        if (transSn == null)
        {
            return HandleResult.Error(ErrCode.E1302);
        }

        // 上料扫码，必须处于进站状态
        if (transSn.Stage != SnStageEnum.In)
        {
            return HandleResult.Error(ErrCode.E1303);
        }

        // 查看是否还存在对应的产品
        var product = await _itemRepo.GetByIdAsync(transSn.ProductId);
        if (product == null)
        {
            return HandleResult.Error(ErrCode.E1304);
        }

        // 检查是否同工位已绑定此关键物料
        var isExists = await _materialConsumeRepo.IsAnyAsync(s => s.Sn == transSn.Sn
            && s.Barcode == input.Barcode
            && s.OpId == transSn.CurOpId
            && !s.IsUnbound);
        if (isExists)
        {
            return HandleResult.Error(ErrCode.E1305);
        }

        // 若有步序，检测该步序是否已上料
        if (input.Step > 0)
        {
            // 校验物料代码对应的关键物料在此工位是否已上件
            var isExists2 = await _materialConsumeRepo.IsAnyAsync(s => s.Sn == transSn.Sn
                    && s.Step == input.Step
                    && s.OpId == transSn.CurOpId
                    && !s.IsUnbound);
            if (isExists2)
            {
                return HandleResult.Error(ErrCode.E1306);
            }
        }

        // 校验是否重复扫码
        string? materialCode = ""; // 物料编号
        var routingItem = await _processQuery.GetRoutingItemAsync(transSn.CurRoutingCode, transSn.CurProcCode);
        if (routingItem != null)
        {
            // 校验条码规则
            var (ok, bom) = await _procBomCheckService.CheckAndGetFeedingAsync(input.Barcode, input.Step, transSn, routingItem!.RoutingId, routingItem!.ProcessId);
            if (!ok)
            {
                return HandleResult.Error(ErrCode.E1307);
            }

            materialCode = bom!.MaterialCode; // 设置物料编号

            // 校验物料来源
            if (routingItem.PolicyCheckProcess.HasFlag(PolicyCheckProcessEnum.CheckBOMSource) && bom.SourceOpId is not null)
            {
                // 物料在指定工位是作为主码存在的。
                var item = await _tranSnRecordRepo.GetLastAsync(s => s.Sn == input.Barcode && s.OpId == bom.SourceOpId);
                if (item == null)
                {
                    return HandleResult.Error(ErrCode.E1308);
                }
                if (item.PassResult == PassResultEnum.NG)
                {
                    return HandleResult.Error(ErrCode.E1309);
                }
            }
        }
        else
        {
            // TODO: 非线上工位，校验 "工位 BOM"。
            return HandleResult.Error(ErrCode.E1307);
        }

        // 新增关键物料
        ProdMaterialConsume material = new()
        {
            Sn = transSn.Sn,
            LineId = transSn.CurLineId,
            LineCode = transSn.CurLineCode,
            OpId = transSn.CurOpId,
            OpCode = transSn.CurOpCode,
            ProcId = transSn.CurProcId,
            ProcCode = transSn.CurProcCode,
            Step = input.Step,
            MaterialCode = materialCode,
            Barcode = input.Barcode,
            FeedingTime = DateTime.Now,
        };
        await _materialConsumeRepo.InsertAsync(material);

        return HandleResult.Ok();
    }
}
